package com.orcamento.service;

import java.util.List;

import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.orcamento.dao.SpentDao;
import com.orcamento.entity.Account;
import com.orcamento.entity.Installment;
import com.orcamento.entity.Invoice;
import com.orcamento.entity.Spent;
import com.orcamento.entity.enums.Month;
import com.orcamento.exception.ConflictException;
import com.orcamento.exception.NotFoundException;
import com.orcamento.exception.ServiceException;
import com.orcamento.service.filter.ListFilter;
import com.orcamento.util.CalendarUtil;
import com.orcamento.util.Money;
import com.orcamento.util.Period;

public class SpentService {

	@Autowired
	private transient SpentDao spentDao;
	@Autowired
	private transient InvoiceService invoiceService;
	@Autowired
	private transient AccountService accountService;

	public Spent save(Spent spent, Long accountId) throws ServiceException {
		try {
			Account account = accountService.findById(accountId);
			spent.setAccount(account);
			return spentDao.save(spent);
		} catch(Exception e) {
			throw new ServiceException("Ocorreum um erro ao salvar o Gasto.", e.getMessage());
		}
	}

	public List<Spent> findByFilter(ListFilter filter, Long accountId) {
		if( filter.isAll() ) {
			return spentDao.findAllAndAccount(accountId);
		}
		Month month = Month.findById( CalendarUtil.getCurrentMonth() );
		DateTime startDate = CalendarUtil.toDate(1, month.id, CalendarUtil.getCurrentYear());
		DateTime endDate = CalendarUtil.toDate(month.maxDays, month.id, CalendarUtil.getCurrentYear(), 23, 59, 59);
		return spentDao.findBetweenAndAccount(startDate, endDate, accountId);
	}
	
	public Spent findById(Long id, Long accountId) throws NotFoundException {
		Spent spent = spentDao.findByIdAndAccount(id, accountId);
		if( spent == null ) {
			throw new NotFoundException(id, "Spent");
		}
		return spent;
	}

	public Spent update(Spent spent, Long accountId) throws ServiceException, NotFoundException {
		Account account = accountService.findById(accountId);
		try {
			Spent spentToSave = findById( spent.getId(), accountId );
			spentToSave.setAccount(account);
			spentToSave.setSpentDay( spent.getSpentDay() );
			spentToSave.setValue( spent.getValue() );
			spentToSave.setEvent( spent.getEvent() );
			return spentDao.save(spentToSave);
		} catch( Exception e ) {
			throw new ServiceException("Ocorreu um erro ao atualizar o Gain com ID: " + spent.getId(), e.getMessage());
		}
	}

	public void delete(Spent spent, Long accountId) throws ServiceException, NotFoundException {
		Spent spentToDelete = findById(spent.getId(), accountId);
		try {
			spentDao.delete(spentToDelete);
		} catch( Exception e ) {
			throw new ServiceException("Ocorreu um erro ao deletar o Spent " + spent.getId(), e.getMessage());
		}
	}

	public List<Spent> findBetween(DateTime start, DateTime limit, Long accountId) {
		return spentDao.findBetweenAndAccount(start, limit, accountId);
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public void save(List<Spent> spents, Long accountId) throws NotFoundException {
		Account account = accountService.findById(accountId);
		for(Spent spent : spents) {
			spent.setAccount(account);
			spentDao.save(spent);
		}
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public Spent purchaseInInstallment(Spent spent, Long accountId) throws NotFoundException, ConflictException, ServiceException {
		Account account = accountService.findById(accountId);
		spent.setAccount(account);
		
		int installmentNumber = 1;
		
		Invoice invoice = invoiceService.findById(spent.getInvoice().getId(), accountId);
		spent.addInstallment( new Installment(installmentNumber++, spent.installmentsPrice(), spent, invoice ) );
		
		Money lastLimit = invoice.getLimit();
		for( ;installmentNumber <= spent.getNumberInstallments(); installmentNumber++ ) {
			Period period = new Period( invoice.getYear(), invoice.getMonth() ).next();
			invoice = invoiceService.findByMonthAndYear(period.getMonth().id, period.getYear(), invoice.getCreditCard(), accountId);
			
			if( invoice == null ) {
				invoice = new Invoice( period.getMonth().id, period.getYear(), spent.getInvoice().getCreditCard() );
				invoice.setAccount(account);
				invoice.setLimit( lastLimit );
				invoiceService.save(invoice, accountId);
			}
			
			spent.addInstallment( new Installment(installmentNumber, spent.installmentsPrice(), spent, invoice) );
			lastLimit = invoice.getLimit();
		}
		
		return spentDao.save(spent);
	}
	
}
